#!/usr/bin/env python
# coding=utf-8
#
#       bill_sprites.py
#       
#       Copyright 2008 Dirk B. <dirkbo@googlemail.com>
#       
#       This program is free software; you can redistribute it and/or modify
#       it under the terms of the GNU General Public License as published by
#       the Free Software Foundation; either version 2 of the License, or
#       (at your option) any later version.
#       
#       This program is distributed in the hope that it will be useful,
#       but WITHOUT ANY WARRANTY; without even the implied warranty of
#       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#       GNU General Public License for more details.
#       
#       You should have received a copy of the GNU General Public License
#       along with this program; if not, write to the Free Software
#       Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
#       MA 02110-1301, USA.
import pygame
import os, sys, time
import random
import math
from pygame.locals import *
from billscreen import billscreen
from ggfx import *

#def load_image(name, colorkey=None):
#   fullname = os.path.join('data/images', name)
#   try:
#       image = pygame.image.load(fullname)
#   except pygame.error, message:
#       print 'Cannot load image:', fullname
#       raise SystemExit, message
#   image = image.convert()
#   if colorkey is not None:
#       if colorkey is -1:
#           colorkey = image.get_at((0,0))
#       image.set_colorkey(colorkey, RLEACCEL)
#   return image, image.get_rect()

class CacheDecorator(object):
    """ Stellt die Möglichkeit bereit, komplexe berechnungen zu cachen. Dazu einfach vor einer 
    Funktion @CacheDecorator() einfügen.
    """
    def __init__(self):
        self.cache = {}
        self.func = None
        
    def cachedFunc(self, *args):
        if args not in self.cache:
            self.cache[args] = self.func(*args)
            #self.cache[args] = res
            #print "Neu berechnet ", self.func, "(", args, ")"
            #print "   ", res
        return self.cache[args]
        
    def __call__(self, func):
        self.func = func
        return self.cachedFunc
        
class CacheSchuttDecorator(object):
    """ Stellt die Möglichkeit bereit, komplexe berechnungen zu cachen. Dazu einfach vor einer 
    Funktion @CacheDecorator() einfügen.
    """
    def __init__(self):
        self.cache = {}
        self.func = None
        
    def cachedFunc(self, *args):
        if args not in self.cache:
            self.cache[args] = self.func(*args)
        return self.cache[args]
        
    def __call__(self, func):
        self.func = func
        return self.cachedFunc
        
@CacheSchuttDecorator()
def framematch(number, lft):
    return int((number * lft)/23.0)
        
class Blockstuck(pygame.sprite.Sprite):
    t_rotate = pygame.transform.rotate
    lft = None
    xof = None
    yof = None
    ltc = None
    
    def __init__(self, x,y, t, lt=3000, gfx=-1, first=37, last=40):
        pygame.sprite.Sprite.__init__(self) #call Sprite intializer
        
        im = []
        if gfx==-1:
            #Blockstücke
            im1, rect = load_image('blockstuck1.bmp', (255,0,255))
            im2, rect = load_image('blockstuck2.bmp', (255,0,255))
            im3, rect = load_image('blockstuck3.bmp', (255,0,255))
            #Billstücke
            im4, rect = load_image('billstuck1.bmp', (255,0,255))
            im5, rect = load_image('billstuck2.bmp', (255,0,255))
            im6, rect = load_image('billstuck3.bmp', (255,0,255))
            im7, rect = load_image('billstuck4.bmp', (255,0,255))
            ##Verbrauchte Mine
            im8, rect = load_image('minedark.bmp', (255,0,255))
            ##Verbrauchter Turbo
            im9, rect = load_image('turbodark.bmp', (255,0,255))
            im.append(im1)
            im.append(im2)
            im.append(im3)
            im.append(im4)
            im.append(im5)
            im.append(im6)
            im.append(im7)
            im.append(im8)
            im.append(im9)
            self.original = im[t]
        else:
            if first != last:
                imageid = random.randrange(last - first +1) + first
                #print "Bild: ", imageid
            else:
                imageid = first
            self.original = gfx.get(imageid)
            self.owninstance = False
            rect= self.original.get_rect()
        
        self.state = 0
        self.image = self.original
        self.image.set_alpha()
        self.allowtrans = False
        self.ltime = lt
        self.images = im
        self.rect = rect.move((x, y))
        self.dizzy = 0
        self.fall = 8 + random.randrange(3)
        #Wenn True, wird das Blockstück nicht entfernt
        self.persistent = False
        
        # Drehung für darstellung
        self.rotate = 0
        #Drehung beim fall
        self.rotation = random.randrange(11) -5
        #Verschiebung nach Links/Rechts beim Fall
        self.drift = (random.randrange(100) / 25 ) -2 
        
    def set_image(self):
        self.image = self.original
        
    def spin(self):
        #self.dizzy = self.dizzy + random.randrange(11)+1
        self.rotate += self.rotation
        if self.rotate >= 360:
            self.rotate = 0
            self.image = self.original
        elif self.rotate < 0:
            self.rotate = 360
            self.image = self.original
        else:
            self.image = Blockstuck.t_rotate(self.original, self.rotate)
        self.rect = self.image.get_rect(center=self.rect.center)

        
    #Neu: lft in Klasse 
    def update(self):
        #ltc: lifetimechange
        if Blockstuck.ltc != None:
            self.ltime += Blockstuck.ltc
            
        if Blockstuck.yof != None:
            #self.rect.top -= Blockstuck.yof - int((self.fall * Blockstuck.lft)/23.0)
            #self.rect.top -= Blockstuck.yof - int((self.fall))
            self.rect.top -= Blockstuck.yof - framematch(self.fall, Blockstuck.lft)
        
        if Blockstuck.xof != None:
            #self.rect.left -= Blockstuck.xof - int((self.drift * Blockstuck.lft)/23.0)
            self.rect.left -= Blockstuck.xof - framematch(self.drift, Blockstuck.lft)
        
        #Neu: LFT in Klasse 
        if Blockstuck.lft != None:
            self.ltime -= Blockstuck.lft
        #Alt: lft lokal
        #self.ltime -= lft
        
        if self.dizzy != -1:
            self.spin()
        
        if self.allowtrans and not self.persistent:
            altime = int(self.ltime / 10)
            if altime <= 255:
                self.image.set_alpha(altime)
                    

        
        
class Billspawner(pygame.sprite.Sprite):
    def __init__(self, x,y, gfx, spt= 3000, rmin=350, rmax=10):
        pygame.sprite.Sprite.__init__(self) #call Sprite intializer
        self.image = gfx.get(36)
        rect = self.image.get_rect()
        self.rect = rect.move((x, y))
        self.spawntime = spt
        self.respawn = self.spawntime -500 +random.randrange(1000)
        self.rmin = rmin
        self.rmax = rmax
        
    def writeXML(self, logger):
        """Returns an XML representation of billspawner to the passed logger
        """
        from xml.sax.xmlreader import AttributesNSImpl
        attr_vals = {
            (None, u'xpos'): unicode(self.rect.left),
            (None, u'ypos'): unicode(self.rect.top),
            (None, u'spawntime'): unicode(self.spawntime),
            (None, u'rotation_min'): unicode(self.rmin),
            (None, u'rotation_max'): unicode(self.rmax),
            }
        attr_qnames = {
            (None, u'xpos'): u'xpos',
            (None, u'ypos'): u'ypos',
            (None, u'spawntime'): u'spawntime',
            (None, u'rotation_min'): u'rotation_min',
            (None, u'rotation_max'): u'rotation_max',
            }
        attrs = AttributesNSImpl(attr_vals, attr_qnames)
        logger.startElementNS((None, u'billspawner'), u'billspawner', attrs)
        #logger.characters(msg)
        logger.endElementNS((None, u'billspawner'), u'billspawner')

        return logger
        
    def update(self, worldofx, worldofy, lft=16 ):
        self.rect.top -= worldofy
        self.rect.left -= worldofx
        #print "Respawn in:", self.respawn
        if self.respawn < 0 and self.respawn != -1000:
            self.respawn = self.spawntime -500 +random.randrange(1000)
        self.respawn -= lft


class XBlock(pygame.sprite.Sprite):
    lft = None
    
    def __init__(self, x,y, gfx=-1, normal=1, construct=6, pieces=9, lastpiece=4):
        pygame.sprite.Sprite.__init__(self) #call Sprite intializer
        
        im = []
        ##leere Grafik, zur Unsichtbarkeit nötig
        im4, rect = load_image('block4.bmp', (255,0,255))
        if gfx == -1:
            im0, rect = load_image('block0.bmp')
            im1, rect = load_image('block1.bmp')
            im2, rect = load_image('block2.bmp')
            im3, rect = load_image('block3.bmp')
            im5, rect = load_image('nblock0.bmp', (255,0,255))
            im6, rect = load_image('nblock1.bmp', (255,0,255))
            im7, rect = load_image('nblock2.bmp', (255,0,255))
        
            im.append(im4)#unsichtbar
            im.append(im0)#normal = 1
            im.append(im1)
            im.append(im2)
            im.append(im3)
            im.append(im0)
            im.append(im5)#aufbau = 6
            im.append(im6)
            im.append(im7)
            #piecesstart = 9
            
            self.firstnormal = 1    
            self.lastnormal = construct - 1 
            self.firstconstruct = construct
            self.lastconstruct = pieces -1
            self.firstpiece= pieces
            self.lastpiece= lastpiece

        else:
            im.append(im4)#unsichtbar
            #Alle Bilder bis pieces hinzufügen
            #print "Übergeben: nf=",normal," cf=", construct, "pf=", pieces, "pl=", lastpiece
            for i in range(normal, pieces):
                im.append(gfx.get(i))
            rec= im[1].get_rect()   
            #print "Anzahl Bilder in X-Block:", str(len(im))
            #print "Anzahl Bilder in MapGFX:", str(len(gfx.gfx))
            self.firstnormal = 1    
            #print "Erstes Normales: ", self.firstnormal, "In Mapgfx: ", normal
            self.lastnormal = construct -normal 
            #print "Letztes Normales: ", self.lastnormal, "In Mapgfx: ", construct-1
            self.firstconstruct = construct -normal +1
            #print "Erstes Konstruktions: ", self.firstconstruct, "In Mapgfx: ", construct
            self.lastconstruct = pieces -normal
            #print "Letztes Konstruktions: ", self.lastconstruct, "In Mapgfx: ", pieces-1
            self.firstpiece= pieces
            #print "Erstes Bruchstück: In Mapgfx: ", pieces-1
            self.lastpiece= lastpiece
            #print "Letztes Bruchstück: In Mapgfx: ", lastpiece 
        self.state = 1
        self.images = im
        self.rect = rect.move((x, y))
        #Wenn True, wird der Block nicht wiederbelebt
        self.persistent = False
        
        self.scount = 10
        self.spawntime = 15000
        self.respawn = self.spawntime
        self.rotation = 0
        self.set_image()
        
    def set_image(self):
        self.scount = 0
        if self.state > 0:
            #if self.state > 6:
            #   print "XBlock State: ", self.state
            self.image = self.images[self.state]
            if self.state == self.lastnormal:
                self.state = self.firstnormal
            elif self.state >= self.lastconstruct:
                self.state = self.firstnormal
            else :
                self.state += 1
        else:
            self.image = self.images[0]
        
            
    def startRespawn(self):
        self.respawn = self.spawntime
        
            
    def update(self, worldofx, worldofy):
        self.rect.top -= worldofy
        self.rect.left -= worldofx
        self.scount += 1
        if self.state == 0 and not self.persistent:
                if XBlock.lft != None:
                    self.respawn -= XBlock.lft
                #print "Respawn in:", self.respawn
                if self.respawn <= 0:
                    self.state = self.firstconstruct
                    self.set_image()
        elif self.scount >= 10:
            self.set_image()
            
class Item(pygame.sprite.Sprite):
    def __init__(self, own =-1, gfx=-1):
        pygame.sprite.Sprite.__init__(self) #call Sprite intializer
        
        im = []
        if gfx == -1:
            ## Bilder von Item 1: Turbo-Ring laden
            im0, rect = load_image('item_turbo_0.bmp', (255,0,255))
            im1, rect = load_image('item_turbo_1.bmp', (255,0,255))
            im2, rect = load_image('item_turbo_2.bmp', (255,0,255))
            im3, rect = load_image('item_turbo_3.bmp', (255,0,255))
            im4, rect = load_image('item_turbo_4.bmp', (255,0,255))
            im5, rect = load_image('item_turbo_5.bmp', (255,0,255))
            im6, rect = load_image('item_turbo_6.bmp', (255,0,255))
            im7, rect = load_image('item_turbo_7.bmp', (255,0,255))
            im8, rect = load_image('item_turbo_8.bmp', (255,0,255))
            ## Bilder von Item 2: Mine laden
            im9, rect = load_image('mine1.bmp', (255,0,255))
            im10, rect = load_image('mine2.bmp', (255,0,255))
            ## Symbol von Item 3: Grüner Bill laden
            im11, rect = load_image('item_grun.bmp', (255,0,255))
            ## Symbol von Item 4: Unverwundbarkeit laden
            im12, rect = load_image('item_gold.bmp', (255,0,255))
            
            im.append(im0)
            im.append(im1)
            im.append(im2)
            im.append(im3)
            im.append(im4)
            im.append(im5)
            im.append(im6)
            im.append(im7)
            im.append(im8)
            im.append(im9)
            im.append(im10)
            im.append(im11)
            im.append(im12)
        else:
            im.append(gfx.get(14))
            rect = im[0].get_rect()
            im.append(gfx.get(15))
            im.append(gfx.get(16))
            im.append(gfx.get(17))
            im.append(gfx.get(18))
            im.append(gfx.get(19))
            im.append(gfx.get(20))
            im.append(gfx.get(21))
            im.append(gfx.get(22))
            im.append(gfx.get(23))
            im.append(gfx.get(24))
            im.append(gfx.get(25))
            im.append(gfx.get(26))
        
        ## Typ: gibt die Art des Items an
        self.typ = 1 + random.randrange(4)
        self.ltime = 200
        ##typspezifische initialisierungen
        if self.typ == 1:
            self.ltime = 20000
            self.state = 0
        if self.typ == 2:
            self.ltime = -1000
            self.state = 9
        if self.typ == 3:
            self.ltime = 1
            self.state = 11
        if self.typ == 4:
            self.ltime = 1
            self.state = 12
        
        ## Gibt den Besitzer an, -1 = eigenes Item
        self.owner = own
        
        ## deployed: gibt an, ob es sich um ein anzuzeigendes Icon
        ##      oder ein auf der Karte liegendes Item handelt
        self.deployed = False
        self.images = im
        self.rect = Rect(((gfx.nresx / 2) - (rect.width / 2), 5),(rect.width,rect.height))
        self.set_image()
        self.scount = 10
        
    def set_image(self):
        if self.typ == 1:
            if self.deployed == True:
                if self.state >= 0:
                    self.image = self.images[self.state]
                    self.state += 1
                ## wenn nachd er obigen addition der state > dem maximalen ist, zurücksetzen
                if self.state >= 9:
                    self.state = 0
            else:
                self.image = self.images[0]
        if self.typ == 2:
            if self.deployed == True:
                if self.state >= 9:
                    self.image = self.images[self.state]
                    self.state += 1
                if self.state >= 11:
                    self.state = 9
            else:
                self.image = self.images[9]
        if self.typ == 3:
            self.image = self.images[11]
        if self.typ == 4:
            self.image = self.images[12]
            
    def update(self, worldofx, worldofy, lft=16):
        self.lft = lft
        if self.deployed == True:
            self.rect.top -= worldofy
            self.rect.left -= worldofx
            if self.ltime > 0:
                self.ltime -= self.lft
        self.scount += 1
        if self.scount >= 5:
            self.scount = 0
            self.set_image()
            
class KollRect:
    def __init__(self, rect):
        self.rect = Rect(rect)
    
    def update(self, worldofx, worldofy):
        self.rect.top -= worldofy
        self.rect.left -= worldofx  
        
    def widthmerge(self, b):
        if self.rect.top == b.rect.top:
            if self.rect.bottom == b.rect.bottom:
                if self.rect.right +1 >= b.rect.left and self.rect.left < b.rect.left:
                    #print "WAltes rect: ", self.rect
                    #print "Zweites rect: ", b.rect
                    self.rect = self.rect.union(b.rect)
                    b.rect.width = 0
                    b.rect.height = 0
                    #print "Neues rect: ", self.rect
                elif b.rect.right +1 >= self.rect.left and b.rect.left < self.rect.left:
                    #print "WAltes rect: ", b.rect
                    #print "zweites rect: ",self.rect
                    b.rect = b.rect.union(self.rect)
                    self.rect.width = 0
                    self.rect.height = 0
                    #print "Neues rect: ", b.rect
                    
    def heightmerge(self, b):
        if self.rect.left == b.rect.left:
            if self.rect.right == b.rect.right:
                if self.rect.bottom +1>= b.rect.top and self.rect.top < b.rect.top:
                    #print "HAltes rect: ", self.rect
                    self.rect = self.rect.union(b.rect)
                    b.rect.width = 0
                    b.rect.height = 0
                    #print "Neues rect: ", self.rect
                elif b.rect.bottom +1>= self.rect.top and b.rect.top < self.rect.top:
                    #print "HAltes rect: ", b.rect
                    b.rect = b.rect.union(self.rect)
                    self.rect.width = 0
                    self.rect.height = 0
                    #print "Neues rect: ", b.rect
            
class Boden(pygame.sprite.Sprite):
    def __init__(self, x,y, typ=0, gfx=-1, first=0, last=0):
        pygame.sprite.Sprite.__init__(self) #call Sprite intializer
        
        self.images = []
        self.imlist = []
        if gfx == -1:
            if typ == 0:
                im, rect = load_image('boden_1_n.bmp', (255,0,255))
                self.rect = Rect((x, y), (rect.width,rect.height))
            elif typ == 1:
                im, rect = load_image('lblock_smw.bmp', (255,0,255))
                self.rect = Rect((x, y), (rect.width,rect.height))
            else:
                im, rect = load_image('lblock_smw.bmp', (255,0,255))
                self.rect = Rect((x, y), (rect.width,rect.height))
            self.images.append(im)
        else:
            if first != last:
                for i in range(first, last):
                    self.images.append(gfx.get(i))
                    #print "Image zu hgrund Object hinzugefügt: ", i
            else:
                self.images.append(gfx.get(first))
            rec = self.images[0].get_rect() 
            self.rect = Rect((x, y), (rec.width,rec.height))    
        
        self.state = 0
        self.scount = 0
        self.maxstate = len(self.images)
        self.image = self.images[0]
    
    def set_image(self):    
        if self.state >= 0:
            self.image = self.images[self.state]
            if self.state < self.maxstate - 1:
                self.state += 1
            else:
                self.state = 0
        else:
            self.image = self.images[0]
                    
    def update(self, worldofx, worldofy):
        self.rect.top -= worldofy
        self.rect.left -= worldofx
        if self.maxstate > 1:
            self.scount += 1
            if self.scount >= 10:
                self.scount = 0
                self.set_image()
                
    def writeXML(self, logger, gfx):
        """Returns an XML representation of billspawner to the passed logger
        """
        from xml.sax.xmlreader import AttributesNSImpl
        attr_vals = {
            (None, u'xpos'): unicode(self.rect.left),
            (None, u'ypos'): unicode(self.rect.top),
            (None, u'typ'): unicode(self.typ),
            }
        attr_qnames = {
            (None, u'xpos'): u'xpos',
            (None, u'ypos'): u'ypos',
            (None, u'typ'): u'typ',
            }
        attrs = AttributesNSImpl(attr_vals, attr_qnames)
        logger.startElementNS((None, u'boden'), u'boden', attrs)
        for i in imlist:
            gfx.writeXML(logger, i)
        #logger.characters(msg)
        logger.endElementNS((None, u'boden'), u'boden')

        return logger
        
class VGrund(Boden):
    def __init__(self, x,y, typ=0, gfx=-1, first=0, last=0):
        pygame.sprite.Sprite.__init__(self) #call Sprite intializer
        
        self.images = []
        if gfx == -1:
            if typ == 0:
                im, rect = load_image('boden_1_n.bmp', (255,0,255))
                self.rect = Rect((x, y), (rect.width,rect.height))
            elif typ == 1:
                im, rect = load_image('lblock_smw.bmp', (255,0,255))
                self.rect = Rect((x, y), (rect.width,rect.height))
            else:
                im, rect = load_image('lblock_smw.bmp', (255,0,255))
                self.rect = Rect((x, y), (rect.width,rect.height))
            self.images.append(im)
        else:
            if first != last:
                for i in range(first, last):
                    self.images.append(gfx.get_new(i))
                    #print "Image zu hgrund Object hinzugefügt: ", i
            else:
                self.images.append(gfx.get_new(first))
            rec = self.images[0].get_rect() 
            self.rect = Rect((x, y), (rec.width,rec.height))    
        
        self.state = 0
        self.scount = 0
        self.maxstate = len(self.images)
        self.image = self.images[0]
        self.trans = 1275
                
    def collide(self, bill, lft):
        if self.rect.colliderect(bill.rect): 
            self.trans -= lft
            if self.trans > 425:
                self.image.set_alpha(self.trans * 0.2)
            else: 
                self.trans = 425
        else:
            if self.trans < 1275:
                self.trans += lft
                if self.trans > 1275:
                    self.trans = 1275
                self.image.set_alpha(self.trans * 0.2)
                
        
@CacheDecorator()
def imgstrint(punkte, font, color):
    return font.render("+"+str(punkte), 1, color)
        
class Points(pygame.sprite.Sprite):
    def __init__(self, bs, x,y, font, punkte=0, ltime= 40, color=(0,255,0)):
        pygame.sprite.Sprite.__init__(self) #call Sprite intializer
        
        # Schriftart initialisieren
        #if font == -1:
        #   font = pygame.font.Font(None, 40)
            
        #Farbe initailisieren
        #wird übergeben als color
        
        #sicherstellen, dass Punkte immer sichtbar bleiben
        self.bs = bs
        self.randx = int(self.bs.xres/50)
        self.randy = int(self.bs.yres/15)
        
        #lebenszeit des scores
        self.lifetime = ltime
        
        self.image = imgstrint(punkte, font, color)
        trect = self.image.get_rect()
        
        self.rect = Rect(x,y, trect.width, trect.height)

                    
    def update(self, worldofx, worldofy):
        self.lifetime -= 1
        if self.rect.top - worldofy > self.randy:
            if self.rect.bottom - worldofy < self.bs.yres - self.randy:
                self.rect.top -= worldofy + 1
        if self.rect.left - worldofx > self.randx:
            if self.rect.right - worldofy < self.bs.xres - self.randx:
                self.rect.left -= worldofx
        
class Fcreateblock(pygame.sprite.Sprite):
    def __init__(self, x,y, gfx):
        pygame.sprite.Sprite.__init__(self) #call Sprite intializer
        
        im = []
        #Alte Bilder-Lademethode
        #if gfx == -1:
        #   im1, rect = load_image('fragleer0.bmp', (255,0,255))
        #   im2, rect = load_image('fragleer1.bmp', (255,0,255))
        #   im.append(im1)
        #   im.append(im2)
        #else:
        im.append(gfx.get(33))  
        rect = im[0].get_rect()     
        im.append(gfx.get(34))
            
        self.ltime = 5000
        self.state = 0
        self.scount = 10
        self.images = im
        self.rect = Rect(x, y,rect.height,rect.width)
        self.set_image()
        
    def set_image(self):
        if self.state >= 0:
            self.image = self.images[self.state]
            if self.state < len(self.images) - 1:
                self.state += 1
            else :
                self.state = 0
        else:
            self.image = self.images[0]
            
    def update(self, worldofx, worldofy, lft=16):
        self.rect.top -= worldofy
        self.rect.left -= worldofx
        self.scount += 1
        self.ltime -= lft
        if self.scount >= 10:
            self.scount = 0
            self.set_image()
        
class Fblock(pygame.sprite.Sprite):
    def __init__(self, x,y, gfx):
        pygame.sprite.Sprite.__init__(self) #call Sprite intializer
        
        im = []
        if gfx == -1:
            im1, rect = load_image('frag1.bmp', (255,0,255))
            im2, rect = load_image('frag2.bmp', (255,0,255))
            im3, rect = load_image('frag3.bmp', (255,0,255))
            im4, rect = load_image('frag4.bmp', (255,0,255))
            im5, rect = load_image('frag5.bmp', (255,0,255))
            im6, rect = load_image('frag6.bmp', (255,0,255))
        
            im.append(im1)
            im.append(im2)
            im.append(im3)
            im.append(im4)
            im.append(im5)
            im.append(im6)
        else:
            im.append(gfx.get(27))
            rect = im[0].get_rect()
            im.append(gfx.get(28))
            im.append(gfx.get(29))
            im.append(gfx.get(30))
            im.append(gfx.get(31))
            im.append(gfx.get(32))

    
        self.state = 0
        self.scount = 0
        self.images = im
        self.rect = Rect(x, y,rect.height,rect.width)
        self.set_image()
        
    def set_image(self):
        if self.state >= 0:
            self.image = self.images[self.state]
            if self.state < len(self.images) - 1:
                self.state += 1
            else :
                self.state = 0
        else:
            self.image = self.images[0]
            
    def update(self, worldofx, worldofy):
        self.rect.top -= worldofy
        self.rect.left -= worldofx
        self.scount += 1
        if self.scount >= 10:
            self.scount = 0
            self.set_image()


class Smoke(pygame.sprite.Sprite):
    def __init__(self, moveto, ltime=75, gfx=-1 ):
        pygame.sprite.Sprite.__init__(self) #call Sprite intializer
        
        im = []
        if gfx == -1:
            im0, rect = load_image('rauch0.bmp', -1)
            im1, rect = load_image('rauch1.bmp', -1)
            im2, rect = load_image('rauch2.bmp', -1)
            im3, rect = load_image('rauch3.bmp', -1)
            im4, rect = load_image('rauch4.bmp', -1)
            im5, rect = load_image('rauch5.bmp', -1)
        else:
            im0 = gfx.get(8)
            rect = im0.get_rect()
            im1 = gfx.get(9)
            im2 = gfx.get(10)
            im3 = gfx.get(11)
            im4 = gfx.get(12)
            im5 = gfx.get(13)
        
        im.append(im0)
        im.append(im1)
        im.append(im2)
        im.append(im3)
        im.append(im4)
        im.append(im5)
        
        self.ltime = ltime
        self.flipafter = int(ltime / 5.0)
        self.state = 0
        self.scount = 0
        self.images = im
        self.rect = rect.move((moveto.left, moveto.top - 10 + random.randrange(20)))
        self.set_image()
        
    def set_image(self):
        if self.state < 5:
            self.image = self.images[self.state]
        self.state += 1
            
    def update(self, worldofx, worldofy):
        self.rect.top -= worldofy
        self.rect.left -= worldofx
        self.scount += 1
        if self.scount >= self.flipafter:
            self.scount = 0
            self.set_image()
            


class Bill(pygame.sprite.Sprite):
    """Zeichnet und Bewegt einen Kugelwilli über den Bildschrim."""
    ## rcache steht allen Instanzen zur Verfügung
    #rcache = CacheDecorator()
    t_rotate = pygame.transform.rotate
    t_flip = pygame.transform.flip
    xof = None
    yof = None
    
    def __init__(self,bs, x=10, y=10, t = 1, rmin=0, rmax=1, gfx=-1, id=-2, lft=-1):
        pygame.sprite.Sprite.__init__(self) #call Sprite intializer
        im = []
        if gfx == -1:
            im1, rect = load_image('bill.bmp', -1)
            im2, rect = load_image('bill_blinz.bmp', -1)
            im3, rect = load_image('bill_faust.bmp', -1)
            im4, rect = load_image('bill_invincible1.bmp', (255,0,255))
            im5, rect = load_image('bill_invincible2.bmp', (255,0,255))
            im6, rect = load_image('bill_invincible3.bmp', (255,0,255))
            im7, rect = load_image('bill_invincible4.bmp', (255,0,255))
            im8, rect = load_image('bill_grun.bmp', (255,0,255))
            im.append(im1)
            im.append(im2)
            im.append(im3)
            im.append(im4)
            im.append(im5)
            im.append(im6)
            im.append(im7)
            im.append(im8)
        else:
            im.append(gfx.get(0))
            rect = im[0].get_rect()
            im.append(gfx.get(1))
            im.append(gfx.get(2))
            im.append(gfx.get(3))
            im.append(gfx.get(4))
            im.append(gfx.get(5))
            im.append(gfx.get(6))
            im.append(gfx.get(7))
            
        self.images = im
        
        ##Eindeutige Identifikationsnummer, zur Zuordnung von Events
        self.id = id
        
        ## Veranlasst das Explodieren des Bills
        self.kill = False
        
        ## gibt die verbleibende Zeit der unsterblichkeit an
        self.invincible = -10
        
        #Bildschirmdaten, um zu ermitteln, wann bei einem eigenen Bill die Welt verschoben werden muss.
        self.bs = bs
        
        #Zeit des letzten Frames, von Update gesetzt, benötigt für alles, was mit timing zu tun hat.
        if lft != -1:
            self.lft = lft
        else:
            self.lft = 16
            
        self.lft_dreh = self.lft / 5
        self.lft_speed = self.lft / 10
        
        #Maxspeed an die aktuelle Auflösung anpassen, wenn gfx übergeben wurde.
        if gfx != -1:
            self.xfaktor = gfx.fx
            self.yfaktor = gfx.fy   
        else:
            self.xfaktor = 1
            self.yfaktor = 1
        
        ## Zeit bis speed auf 100 gesetzt wird/Bill abgefeuert wird
        self.shoot = -1000
        
        #Bild und Rechteck für Sprite setzen
        #self.image = self.images[0]
        self.rect = Rect(x,y,rect.width, rect.height)
        
        #Art des Bills, siehe unten
        self.typ = t
        ## Typ:
        ## 0 - Eigener Bill, verschiebt die Welt
        ## 1 - Fremdbill, wird mit der Welt verschoben, fliegt einfach geradeaus
        
        #Verwendete Grafik
        self.state = 0  
                
        ##Besitzer des Bills
        self.owner = 0
        
        ## False verbietet es, dass Bill bewegt wird
        self.allowmove = True
        
        ##Visible = False > Bill wird nicht gerendert
        self.visible = True
        
        ##bounce = True: abill prallt an wänden und der Levelgrenze ab
        self.bounce = False
        
        #erlaubte höchstgeschwindigkeit in pixel/10 ms
        #self.maxspeed = 25
        self.maxspeed = 7.0
        
        self.oldspeed = -1
        
        self.keepinscreen = False
        if self.typ == 0:
            self.rect.topleft = 512, 384
            self.set_speed(30,self.lft)
            self.rotation = rmin + random.randrange(rmax)
            self.steigung = 0.0;
            self.richtung = 1.0;
            self.xdir = 1;
            self.id = 1
            self.keepinscreen = True
        
        #nicht abprallender schwarzer bill
        if self.typ == 1:
            self.set_speed(30, self.lft)
            self.maxspeed = 4.6
            self.rotation = rmin + random.randrange(rmax)
            if self.rotation >= 360:
                self.rotation -= 360
            self._rot_right()
            self.keepinscreen = False
            
        ## Grüner Bill, der abprallt
        if self.typ == 2:
            self.set_speed(15,self.lft)
            self.maxspeed = 8.5
            self.bounce = True
            self.rotation = rmin + random.randrange(rmax)
            if self.rotation >= 360:
                self.rotation -= 360
            self._rot_right()
            self.keepinscreen = False
            
        
        #rahmen in dem Bill bewegt wird, und nicht die Welt
        self.mrect = Rect(384,288,256,192)
        if self.typ == 0:
            xp = int(self.bs.xres / 2.6)
            yp = int(self.bs.yres / 2.6)
            xw = self.bs.xres - (2 * xp)
            yw = self.bs.yres - (2 * yp)
            self.mrect = Rect(xp, yp, xw, yw)       
        
        #soviel muss der rest der Welt bewegt werden, wenn bill aus rahmen käme
        self.worldx = 0
        self.worldy = 0
        
        
        self.dizzy = 0
        
        """rot_type = 1 > links drehen
           rot_type = 2 > rechts drehen
           rot_type = 0 > nicht drehen  
        """
        self.rot_type = 0
        """ sped_type = 1 > Beschleunigen
            sped_type = 2 > Bremsen
            sped_type = 0 > Geschwindigkeit beibehalten
        """
        self.sped_type = 0;
        #faktor für y-richtung
        
        #bild berechnen
        self.set_image()
        
        self.smokecount = 1
        #Frame basiert:
        self.smokecountmax = 5
        #self.smokecountmax = 150
        self.smokecountmax = 60 / (0.1 + self.speed * self.lft_speed * 1.1)
            

    def set_image(self):
        #Center ändert sich dauerns, daher jedesmal neu berechnen
        center = self.rect.center
        #Alternativ, höhe und breite speichern und von top/left abziehen. Schneller?
        
        newim = self.images[0]
        if self.typ != 2:
            if self.invincible <= 0:
                if self.state == 0:
                    newim = self.images[0]
                elif self.state == 1:
                    newim = self.images[1]
                elif self.state == 2:
                    newim = self.images[2]
            else:
                if self.state < 3:
                    self.state = 3
                newim = self.images[self.state]
                if self.invincible < 3000:
                    if self.state == 4:
                        newim = self.images[0]
                if self.invincible < 1000:
                    if self.state == 6:
                        newim = self.images[0]
                self.state += 1
                
                if self.state > 6:
                    self.state = 3
                
        else:   
            newim = self.images[7]
            
        #Wenn nötig, Bild spiegeln
        if self.rotation > 90 and self.rotation < 270:
            newim = Bill.t_flip(newim,False,True)
        #Bild drehen
        self.image = Bill.t_rotate(newim, self.rotation)
        self.rect = self.image.get_rect(center=center)      
        
    def set_speed(self, speed, lft=-1):
        self.speed = float(speed)
        if lft != -1:
            if self.lft != lft:
                self.lft = lft
                #Alle lft-abhängigen Werte neu berechnen:
                self.lft_dreh = float(float(self.lft) / 5)
                self.lft_speed = float(float(self.lft) / 10.0)
        
    
    def update(self, lft=16):
        if self.lft != lft:
            self.lft = lft
            #Alle lft-abhängigen Werte neu berechnen:
            self.lft_dreh = float(float(self.lft) / 5)
            self.lft_speed = float(float(self.lft) / 10.0)
        if self.invincible > 0:
            if self.invincible > 1000:
                #Wenn unverwundbar, Hohe geschwindigkeit beibehalten
                if self.speed < 8.4:
                    self.speed = 8.4
            #Verbleibende Unverwundbarkeitszeit reduzieren
            self.invincible -= self.lft
        if self.allowmove == False:
            self.rot_type = 0
            self.sped_type = 0
            self.speed = 0
            self.rotation = 0
        if self.shoot != -1000:
            self.shoot -= lft
            if self.shoot <= 0:
                self.speed = 30
                self.shoot = -1000
                self.allowmove = True
                self.visible = True
        self._walk()
        if self.speed != self.oldspeed:
            self.oldspeed = self.speed
            #self.smokemax = self._ret_smokemax(self.speed, self.lft)
            self.smokecountmax = 60 / (0.1 + self.speed * self.lft_speed * 1.1)
            
        nstate = -1
        if self.invincible < 1:
            z = random.randrange(40)+1
            if z == 19 or z == 18:
                nstate = 1
            elif z == 20:
                nstate = 2
            else:
                nstate = 0
        #Falls sich der Status geändert hat, ändern
        if nstate != self.state:
            if self.invincible < 1:
                self.state = nstate
            self.set_image()
        if self.typ != 0:
            self.rect.top -= Bill.yof
            self.rect.left -= Bill.xof
        

    def _walk(self):
        # Drehungen ausführen
        if(self.rot_type == 1):
            self._rot_left();
        elif(self.rot_type == 2):
            self._rot_right();
        
        # Wenn der Gasknopf gedrückt ist    
        if(self.sped_type == 1 and self.speed < self.maxspeed ):
            self.speed += 0.1;
            #self.get_smokemax()
            #wenn der Gasknopf losgelassen ist
        elif(self.sped_type == 2 and self.speed > 0 ):
            self.speed -= 0.1;
            if self.speed < 0:
                self.speed = 0
            #self.get_smokemax()
        
        # wenn bill schneller als zulässig, abbremsen   
        if self.speed > self.maxspeed:
            zuvielspeed = self.speed - self.maxspeed
            self.speed -= 0.2 * zuvielspeed
            #self.get_smokemax()
            
        if self.speed > 0:  
            #wenn sich der willi in der Mitte befindet, ihn bewegen 
            yspeed = round(self.speed * self.lft_speed * self.steigung)
            xspeed = round(self.speed * self.lft_speed * self.richtung)
            newpos = self.rect.move((xspeed, yspeed))
            
            #Fremden Bills ermöglichen, aus dem Bild zu fliegen, eigenem Bill verbieten
            self.worldy = 0
            self.worldx = 0
            if self.typ == 0 and self.keepinscreen == True:
                Bill.yof = 0
                Bill.xof = 0
                # Y auf weltverschiebung prüfen
                if newpos.bottom < self.mrect.top:
                    dif = self.mrect.top - newpos.bottom
                    Bill.yof = -dif
                    newpos.top += dif
                elif  newpos.top > self.mrect.bottom:
                    dif = newpos.top - self.mrect.bottom
                    Bill.yof = dif
                    newpos.bottom -= dif
            
                #x auf Weltverschiebung prüfen
                if newpos.right < self.mrect.left:
                    dif = self.mrect.left - newpos.right
                    Bill.xof= -dif
                    newpos.left += dif
                elif newpos.left > self.mrect.right:
                    dif = newpos.left - self.mrect.right
                    Bill.xof = dif
                    newpos.right -= dif
            
            self.rect = newpos
            #smokecount aktualisieren
            self.smokecount += 1
            if self.smokecount >= self.smokecountmax:
                self.smokecount = 0
            #ansonsten die Welt bewegen

        else:
            # Wenn der Bill steht, die Welt nicht bewegen
            if self.typ == 0:
                Bill.xof = 0
                Bill.yof = 0
            
    def _rot_left(self):
        center = self.rect.center
        self.rotation = int(self.rotation + self.lft_dreh)
        #Steigung berechnen 
        if self.rotation >= 360:
            self.rotation -=360
        elif self.rotation <= 0:
            self.rotation += 360
        ### Steigung mit Hilfe einer cache-baren Funktion berechnen.    
        self.richtung = self._calc_richtung(self.rotation, self.xfaktor)
        self.steigung = self._calc_steigung(self.rotation, self.yfaktor)
        
        # Bild aktualisieren
        self.set_image()
    
        
    def _rot_right(self):
        center = self.rect.center
        self.rotation = int(self.rotation - self.lft_dreh)
        #Steigung berechnen 
        if self.rotation >= 360:
            self.rotation -=360
        elif self.rotation <= 0:
            self.rotation += 360
        ### Steigung mit Hilfe einer cache-baren Funktion berechnen.    
        self.richtung = self._calc_richtung(self.rotation, self.xfaktor)
        self.steigung = self._calc_steigung(self.rotation, self.yfaktor)
        # Bild aktualisieren
        self.set_image()    
    
    @CacheDecorator()
    def _calc_richtung(rotation, xfaktor):
        return math.cos(math.radians(rotation)) * xfaktor
    
    @CacheDecorator()
    def _calc_steigung(rotation, yfaktor):
        return -math.sin(math.radians(rotation)) * yfaktor
        
        
    # legt fest, in welcher Art eine Drehung zu erfolgen hat
    def det_turn(self, rect):   
        ##kollision passiert mit oberkante des anderen objektes
        colltop = self.rect.collidepoint(self.rect.centerx, rect.top)
        collbottom = self.rect.collidepoint(self.rect.centerx, rect.bottom)
        collleft = self.rect.collidepoint(rect.left, self.rect.centery)
        collright = self.rect.collidepoint(rect.right, self.rect.centery)
        
                    
        sum = int(colltop) + int(collbottom) + int(collleft) + int(collright)
        
        #wenn mehrere möglichkeiten gefunden, ist weitere differenzierung nötig:
        if sum > 1:
            #print "Kollisionen VH:", colltop, collbottom, collleft, collright, "Summe: ", sum
            sektor = 0
            if self.rotation >= 0 and self.rotation < 90:
                sektor = 1
            elif self.rotation >=90 and self.rotation < 180:
                sektor = 2
            elif self.rotation >= 180 and self.rotation < 270:
                sektor = 3
            elif self.rotation >= 180 and self.rotation < 360:
                sektor = 4
                
            if sektor == 1:
                collright = 0
                colltop = 0
            if sektor == 2:
                collleft = 0
                colltop = 0
            if sektor == 3:
                collleft = 0
                collbottom = 0
            if sektor == 4:
                collright = 0
                collbottom = 0
            
            sum = int(colltop) + int(collbottom) + int(collleft) + int(collright)
            #print "Kollisionen N1:", colltop, collbottom, collleft, collright, "Summe: ", sum
            
            if sum > 1:
                xa = rect.centerx - self.rect.centerx
                ya = rect.centery - self.rect.centery
                xb = self.rect.centerx - rect.centerx
                yb = self.rect.centery - rect.centery
                if colltop and collleft:
                    if xa > ya:
                        colltop = 1
                        collleft = 0
                    else:
                        colltop = 0
                        collleft = 1
                if colltop and collright:
                    if xb > yb:
                        colltop = 1
                        collright = 0
                    else:
                        colltop = 0
                        collright= 1
                if collbottom and collleft:
                    if xa > ya:
                        collbottom = 1
                        collleft = 0
                    else:
                        collbottom = 0
                        collleft = 1
                if collbottom and collright:
                    if xb > yb:
                        collbottom = 1
                        collright = 0
                    else:
                        collbottom = 0
                        collright= 1
    
                sum = int(colltop) + int(collbottom) + int(collleft) + int(collright)
                #print "Kollisionen N2:", colltop, collbottom, collleft, collright, "Summe: ", sum
        
        if sum == 1:
            if colltop:
                #self.turn(1)
                #kollision mit oberseite
                self._turny()
                self.uncollide(rect)
            elif collbottom:
                #self.turn(1)
                #kollision mit unterseite
                self._turny()
                self.uncollide(rect)
            elif collleft:
                #self.turn(0)
                #kollision mit linker seite
                self._turnx()
                self.uncollide(rect)
            elif collright:
                #self.turn(0)
                #kollision mit rechter seite
                self._turnx()
                self.uncollide(rect)
            self.set_image()
            
    def uncollide(self, nrect):
        if self.rect.colliderect(nrect):
            collision = True
            yspeed = int((self.steigung * self.lft_speed * self.speed))
            xspeed = int((self.richtung * self.lft_speed * self.speed))
            newpos = self.rect
            while collision == True:
                #Bewegen, _walk ist niicht aufrufbar, ergäbe endlosschleife bei invisible=true
                #wenn sich der willi in der Mitte befindet, ihn bewegen     
                newpos = newpos.move((xspeed, yspeed))
                if newpos.colliderect(nrect):
                    collision = True
                else:
                    collision = False
            self.rect = newpos
            
    @CacheDecorator()
    def _get_rotation(x,y):
        #print "X:", x, ",Y:",y
        #Je nach Quadrant
        fab = math.fabs(float(y)/float(x))
        #print " fabs(X/Y):", fab
        ata = math.atan(fab)
        #print " atan(fabs(x/y)):", ata
        deg = math.degrees(ata)
        #print " degrees(acos(x/y)):", deg
        
        #else:
        #   print "degrees(acos(x/y)):", math.degrees(math.acos(math.fabs(float(x)/y)))
        #   return math.degrees(math.atan(math.fabs(float(x)/y)))
            #Stimmt
        if x > 0 and y > 0:
            return 360 - deg
        elif x > 0 and y < 0:
            return deg
        elif x < 0 and y > 0:
            return 180 + deg
        elif x < 0 and y < 0:
            return 180 - deg
        else:
            return deg
        
    
    def _turnx(self):
        """Kollision mit einem Rechteck von links oder rechts
        """
        #Steigung = Steigung
        self.richtung = self.richtung * (-1)
        #Rotation neu berechnen
        self.rotation = self._get_rotation(self.richtung, self.steigung)
        
    def _turny(self):
        """Kollision mit einem Rechteck von oben oder unten
        """
        #Richtung bleibt unverändert
        self.steigung = self.steigung * (-1)
        #Rotation neu berechnen
        self.rotation = self._get_rotation(self.richtung, self.steigung)
        

    def turn(self, t=1):
        #self.richtung = self.richtung * -1
        if t == 0:
            self._turnx()
        else:
            self._turny() 
        
    def reset(self):
        self.speed = 0
        self.rotation = 0
        self.rot_type = 0
        self.sped_type = 0
        
        #Steigung berechnen
        if self.rotation >= 360:
            self.rotation -=360
        elif self.rotation <= 0:
            self.rotation += 360
        ### Steigung mit Hilfe einer cache-baren Funktion berechnen.    
        self.richtung = self._calc_richtung(self.rotation, self.xfaktor)
        self.steigung = self._calc_steigung(self.rotation, self.yfaktor)
        
        self.invincible = -1
        self.set_image()
        
    
